#ifndef _TLBCMP_H
#define _TLBCMP_H

#include <iostream>
#include <fstream>
#include <sstream>
#include <memory>
#include <cassert>
#include <string.h>
#include <stdlib.h>
#include <zlib.h>
#include <math.h>
#include <vector>
#include <pthread.h>


#include "../../engine/address.hpp"
#include "../../utils/libcache/CacheWriteBack.hpp"

#include "constants.h"
#include "memory.h"
#include "trace.h"
#include "core.h"


class TLBCMP {
    CacheWriteBack<address_t>  *_TLB2[32];
    std::vector<Core *>  _core;
    std::vector <pthread_t>_pthread;
    std::vector<int> _appThreads;
    pthread_mutex_t _mutexDiDi;

    uint32_t _lineSize;
    uint32_t _pageSize;
    uint32_t _TLB1Size;
    uint32_t _TLB2Size;
    uint32_t _TLB1Nways,_TLB2Nways;
    uint32_t _TLB2SetBits;
    uint32_t _TLB2AssocBits;
    uint32_t _pagesInRange;
    uint32_t _blockBits;
    uint32_t _maskBlock;
    uint32_t _current_threads;
    uint32_t _threads;
    uint32_t _app;

    uint32_t _TLB2Hits, _TLB2HitInvalidate, _TLB2Misses,_TLB2Replacements;
    uint32_t _TLB2Accesses,_TLB2WriteBacks,_TLB2Evicted;

    public:
        TLBCMP(uint32_t threads,uint32_t lineSize,uint32_t pageSize,
                uint32_t pagesInRange,uint32_t TLB1Size, uint32_t TLB2Size,
                uint32_t TLB1Nways, uint32_t TLB2Nways);
        ~TLBCMP();
        void reset();
        void setAppTrace(Trace *t,uint32_t threads);
        void startCore(int core);
        void execute(void);
        void updateL1(address_t addr,uint32_t thread);
        void flushTLB2(address_t evictPage,uint32_t thread);

        uint32_t getPageInDirectory(address_t page);
        address_t resetPageInRange(address_t addr);
        address_t setPageInRange(address_t addr,uint32_t page);

        void evictDiDi(uint32_t appid,address_t evictPage,uint32_t _threadid);
        void evictTLB1Range(uint32_t appid,address_t evictPage,Directory dirEvict);
        void addEntryDiDi(uint32_t appid,address_t addr,uint32_t threadid);
        address_t appendVPID(address_t addr,uint32_t appid);
        address_t removeVPID(address_t addr);
        uint32_t getApplicationVPID(address_t addr);

        uint32_t getThreads() const;
        void getAppName(char *appname,uint32_t tid) const;
        uint32_t getTLB1Accesses(uint32_t tid) const;
        uint32_t getTLB1EvictedPages(uint32_t tid) const;
        uint32_t getTLB1ForcedEvictedPages(uint32_t tid) const;
        uint32_t getTLB2EvictedPages() const;
        uint32_t getLineSize() const;
        uint32_t getPagesInRange() const;
        uint32_t getTLB1Size() const;
        uint32_t getTLB2Size() const;
        uint32_t getTLB1Nways() const;
        uint32_t getTLB2Nways() const;
        uint32_t getTLB2AssocBits() const;
        uint32_t getTLB2SetBits() const;
        uint32_t getTLB1Hits(int tid) const;
        uint32_t getTLB1HitInvalidate(int tid) const;
        uint32_t getTLB1Misses(int tid) const;
        uint32_t getTLB1Replacements(int tid) const;
        uint32_t getTLB1Evictions(int tid) const;
        uint32_t getTLB2Hits() const;
        uint32_t getTLB2HitInvalidate() const;
        uint32_t getTLB2Accesses() const;
        uint32_t getTLB2Misses() const;
        uint32_t getTLB2Replacements() const;
        uint32_t getTLB2WriteBacks() const;

};

#include "tlbCMP.ipp"

#endif
